//
//  main.c
//  vCalc
//
//  Created by Vu Q. Du on 6/2/11.
//  Copyright IFISolution 2011. All rights reserved.
//

#include <Carbon/Carbon.h>
#include "evaluationEx.h"
#include "in2post.h"

//--------------------------------------------------------------------------------------------
//--Custom code--//
#define myCalculator9	'xxx9'
#define myCalculator8	'xxx8'
#define myCalculator7	'xxx7'
#define myCalculator6	'xxx6'
#define myCalculator5	'xxx5'
#define myCalculator4	'xxx4'
#define myCalculator3	'xxx3'
#define myCalculator2	'xxx2'
#define myCalculator1	'xxx1'
#define myCalculator0	'xxx0'

#define myCalculatorComa	'xxx,'
#define myCalculatorOpen	'xxx('
#define myCalculatorClose	'xxx)'
#define myCalculatorMemo	'xxxm'

#define myCalculatorAdd		'xxx+'
#define myCalculatorSub		'xxx-'
#define myCalculatorMul		'xxx*'
#define myCalculatorDiv		'xxx/'

#define myCalculatorEqal	'equa'
#define myCalculatorClear	'xclr'

#define   kControlSignature     'RSLT'
#define   kStringOutControlID       1
//--End custom code--//
//--------------------------------------------------------------------------------------------

static OSStatus        AppEventHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon );
static OSStatus        HandleNew();
static OSStatus        WindowEventHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon );

static IBNibRef        sNibRef;

void myCalculatorHanler(WindowRef window, CFStringRef inCF);
void CustomSetTextData(ControlHandle ctrlText, CFStringRef *inCFS);
void CustomGetTextData(ControlHandle ctrlText, CFStringRef *inCFS);
/*Evaluate an expression*/
void CustomEvaluateExp(ControlHandle ctrlText, CFStringRef inCFS);

//--------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    OSStatus                    err;
    static const EventTypeSpec    kAppEvents[] =
    {
        { kEventClassCommand, kEventCommandProcess }
    };

    // Create a Nib reference, passing the name of the nib file (without the .nib extension).
    // CreateNibReference only searches into the application bundle.
    err = CreateNibReference( CFSTR("main"), &sNibRef );
    require_noerr( err, CantGetNibRef );
    
    // Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar
    // object. This name is set in InterfaceBuilder when the nib is created.
    err = SetMenuBarFromNib( sNibRef, CFSTR("MenuBar") );
    require_noerr( err, CantSetMenuBar );
    
    // Install our handler for common commands on the application target
    InstallApplicationEventHandler( NewEventHandlerUPP( AppEventHandler ),
                                    GetEventTypeCount( kAppEvents ), kAppEvents,
                                    0, NULL );
    
    // Create a new window. A full-fledged application would do this from an AppleEvent handler
    // for kAEOpenApplication.
    HandleNew();
    
    // Run the event loop
    RunApplicationEventLoop();

CantSetMenuBar:
CantGetNibRef:
    return err;
}

//--------------------------------------------------------------------------------------------
static OSStatus
AppEventHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon )
{
    OSStatus    result = eventNotHandledErr;
    
    switch ( GetEventClass( inEvent ) )
    {
        case kEventClassCommand:
        {
            HICommandExtended cmd;
            verify_noerr( GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd ) );
            
            switch ( GetEventKind( inEvent ) )
            {
                case kEventCommandProcess:
                    switch ( cmd.commandID )
                    {
                        case kHICommandNew:
                            result = HandleNew();
                            break;
                            
                        // Add your own command-handling cases here
                        
                        default:
                            break;
                    }
                    break;
            }
            break;
        }
            
        default:
            break;
    }
    
    return result;
}

//--------------------------------------------------------------------------------------------
DEFINE_ONE_SHOT_HANDLER_GETTER( WindowEventHandler )

//--------------------------------------------------------------------------------------------
static OSStatus
HandleNew()
{
    OSStatus                    err;
    WindowRef                    window;
    static const EventTypeSpec    kWindowEvents[] =
    {
        { kEventClassCommand, kEventCommandProcess }
    };
    
    // Create a window. "MainWindow" is the name of the window object. This name is set in 
    // InterfaceBuilder when the nib is created.
    err = CreateWindowFromNib( sNibRef, CFSTR("MainWindow"), &window );
    require_noerr( err, CantCreateWindow );

    // Install a command handler on the window. We don't use this handler yet, but nearly all
    // Carbon apps will need to handle commands, so this saves everyone a little typing.
    InstallWindowEventHandler( window, GetWindowEventHandlerUPP(),
                               GetEventTypeCount( kWindowEvents ), kWindowEvents,
                               window, NULL );
    
    // Position new windows in a staggered arrangement on the main screen
    RepositionWindow( window, NULL, kWindowCascadeOnMainScreen );
    
    // The window was created hidden, so show it
    ShowWindow( window );
    
CantCreateWindow:
    return err;
}

//--------------------------------------------------------------------------------------------
static OSStatus
WindowEventHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon )
{
    OSStatus    err = eventNotHandledErr;
	/* begin custom code*/
	WindowRef    window; 
	window = (WindowRef)inRefcon;
    /*end custom code*/
    switch ( GetEventClass( inEvent ) )
    {
        case kEventClassCommand:
        {
            HICommandExtended cmd;
            verify_noerr( GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof( cmd ), NULL, &cmd ) );
            
            switch ( GetEventKind( inEvent ) )
            {
                case kEventCommandProcess:
                    switch ( cmd.commandID )
                    {
						case myCalculator0:
							myCalculatorHanler(window, CFSTR("0"));
							break;
						case myCalculator1:
							myCalculatorHanler(window, CFSTR("1"));
							break;
						case myCalculator2:
							myCalculatorHanler(window, CFSTR("2"));;
							break;
						case myCalculator3:
							myCalculatorHanler(window, CFSTR("3"));;
							break;
						case myCalculator4:
							myCalculatorHanler(window, CFSTR("4"));;
							break;
						case myCalculator5:
							myCalculatorHanler(window, CFSTR("5"));;
							break;
						case myCalculator6:
							myCalculatorHanler(window, CFSTR("6"));;
							break;
						case myCalculator7:
							myCalculatorHanler(window, CFSTR("7"));;
							break;
						case myCalculator8:
							myCalculatorHanler(window, CFSTR("8"));;
							break;
						case myCalculator9:
							myCalculatorHanler(window, CFSTR("9"));;
							break;
							//----****----
						case myCalculatorComa:
							myCalculatorHanler(window, CFSTR("."));;
							break;
						case myCalculatorOpen:
							myCalculatorHanler(window, CFSTR("("));;
							break;
						case myCalculatorClose:
							myCalculatorHanler(window, CFSTR(")"));;
							break;
						//case myCalculatorMemo:
						//	myCalculatorHanler(window, CFSTR("M"));;
							break;
							//----****----
						case myCalculatorAdd:
							myCalculatorHanler(window, CFSTR("+"));;
							break;
						case myCalculatorSub:
							myCalculatorHanler(window, CFSTR("-"));;
							break;
						case myCalculatorMul:
							myCalculatorHanler(window, CFSTR("*"));;
							break;
						case myCalculatorDiv:
							myCalculatorHanler(window, CFSTR("/"));;
							break;
							//----****----
						case myCalculatorEqal:
							myCalculatorHanler(window, CFSTR("Ans"));
							break;
						case myCalculatorClear:
							myCalculatorHanler(window, CFSTR("Clear"));;
							break;
							

                        // Add your own command-handling cases here
                        
                        default:
                            break;
                    }
                    break;
            }
            break;
        }
            
        default:
            break;
    }
    
    return err;
}

void myCalculatorHanler(WindowRef window, CFStringRef inCF){

	ControlHandle   ctrlStringTextEdit; 
	ControlID       stringOutControlID = { kControlSignature, kStringOutControlID }; 
	CFStringRef     theString = CFSTR("");
	GetControlByID( window, &stringOutControlID, &ctrlStringTextEdit );
	CFArrayRef	cfArrayToConv;
	void*		arrData[2];
	
	/*Clear text input*/
	if(CFStringCompare(inCF, CFSTR("Clear"), kCFCompareCaseInsensitive) == kCFCompareEqualTo){
		CustomSetTextData( ctrlStringTextEdit, &theString);
	} 
	
	else if(CFStringCompare(inCF, CFSTR("Ans"), kCFCompareCaseInsensitive) == kCFCompareEqualTo){
		CustomGetTextData(ctrlStringTextEdit, &theString);
		CustomEvaluateExp(ctrlStringTextEdit, theString);
	}
	
	else{
		CustomGetTextData(ctrlStringTextEdit, &theString);
		arrData[0] = (void*) theString;
		arrData[1] = (void*) inCF;
		cfArrayToConv = CFArrayCreate (
									   kCFAllocatorDefault,
									   (void*) arrData,
									   2,
									   &kCFTypeArrayCallBacks);
		theString = CFStringCreateByCombiningStrings(kCFAllocatorDefault, cfArrayToConv , CFSTR(""));
		CustomSetTextData( ctrlStringTextEdit, &theString );
	}
}

void CustomSetTextData(ControlHandle ctrlText, CFStringRef *inCFS){
	SetControlData( ctrlText, 
                   kControlEntireControl, 
                   kControlEditTextCFStringTag, 
                   sizeof( CFStringRef ), 
                   inCFS );
	DrawOneControl( ctrlText);
}

void CustomGetTextData(ControlHandle ctrlText, CFStringRef *inCFS){
	GetControlData( ctrlText, 
				   kControlEntireControl, 
				   kControlEditTextCFStringTag, 
				   sizeof( CFStringRef ), 
				   inCFS,
				   NULL );
}

void CustomEvaluateExp(ControlHandle ctrlText, CFStringRef inCFS){
	/*convert CFString to CString to use some token functions*/
	char buffer[512];
	char bufferOut[512];
	CFStringGetCString(inCFS, buffer, sizeof( buffer ), kCFStringEncodingUTF8 );
	CFStringRef vString;
	
	infix2postfix(&buffer[0],&bufferOut[0],1);
	int result = evaluate(&bufferOut[0]);
	vString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR( "%d" ), result);
	CustomSetTextData(ctrlText, &vString);
}